package com.jay.media.music.core.player

import android.content.Context
import android.media.session.PlaybackState
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.analytics.AnalyticsCollector
import com.google.android.exoplayer2.metadata.Metadata
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.trackselection.ExoTrackSelection
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
import com.google.android.exoplayer2.util.Clock
import com.google.android.exoplayer2.util.EventLogger

/**
 * @author xuzhou4
 * @date 2021/12/3
 */
abstract class AbstractAudioPlayer(var context: Context, var playLooper: Looper?) : IMediaPlayer {

    companion object {
        const val MSG_PLAYER_PROGRESS = 1 shl 1
        const val TAG = "AudioPlayer"
    }

    private var mCurrentPlayState: Int = PlaybackState.STATE_NONE
    private lateinit var mExoPlayer: SimpleExoPlayer
    private var mPlayerHandler: PlayerHandler =
        PlayerHandler((if (playLooper == null) Looper.getMainLooper() else playLooper)!!)

    private var mPlayerEventListener: IPlayerEventListener? = null
    private var mAudioListener: IAudioListener? = null

    private var mEventListener = object : Player.Listener {

        override fun onMetadata(metadata: Metadata) {
            super.onMetadata(metadata)
            Log.d(TAG, "onMetadata() called with: metadata = $metadata")
        }

        override fun onPlayerError(error: PlaybackException) {
            Log.d(TAG, "onPlayerError() called with: error = $error")
        }

        override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
            super.onPlayWhenReadyChanged(playWhenReady, reason)
            Log.d(
                TAG,
                "onPlayWhenReadyChanged() called with: playWhenReady = $playWhenReady, reason = $reason"
            )
            if (playWhenReady) {
                if (mCurrentPlayState == PlaybackState.STATE_PAUSED) {
                    mCurrentPlayState = PlaybackState.STATE_PLAYING
                    mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                    mPlayerHandler.removeMessages(MSG_PLAYER_PROGRESS)
                    mPlayerHandler.sendEmptyMessage(MSG_PLAYER_PROGRESS)
                }
            } else {
                mCurrentPlayState = PlaybackState.STATE_PAUSED
                mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                mPlayerHandler.removeMessages(MSG_PLAYER_PROGRESS)
            }
        }

        override fun onPlaybackStateChanged(playbackState: Int) {
            super.onPlaybackStateChanged(playbackState)
            Log.d(TAG, "onPlaybackStateChanged() called with: playbackState = $playbackState")
            when (playbackState) {
                Player.STATE_IDLE -> {
                    mCurrentPlayState = PlaybackState.STATE_NONE
                    mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                }
                Player.STATE_BUFFERING -> {
                    if (mExoPlayer.playWhenReady) {
                        mCurrentPlayState = PlaybackState.STATE_PLAYING
                        mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                        mPlayerHandler.removeMessages(MSG_PLAYER_PROGRESS)
                        mPlayerHandler.sendEmptyMessage(MSG_PLAYER_PROGRESS)
                    } else {
                        mCurrentPlayState = PlaybackState.STATE_BUFFERING
                        mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                    }
                }
                Player.STATE_READY -> {
                    if (mExoPlayer.playWhenReady) {
                        mCurrentPlayState = PlaybackState.STATE_PLAYING
                        mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                        mPlayerHandler.removeMessages(MSG_PLAYER_PROGRESS)
                        mPlayerHandler.sendEmptyMessage(MSG_PLAYER_PROGRESS)
                    } else {
                        mCurrentPlayState = PlaybackState.STATE_PAUSED
                        mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                        mPlayerHandler.removeMessages(MSG_PLAYER_PROGRESS)
                    }
                }
                Player.STATE_ENDED -> {
                    mCurrentPlayState = PlaybackState.STATE_STOPPED
                    mPlayerEventListener?.onPlayStateChanged(mCurrentPlayState)
                    mPlayerEventListener?.onPlayCompleted()
                }
                else -> {
                }
            }
        }
    }

    init {
        mPlayerHandler.post {
            val bandwidthMeter = DefaultBandwidthMeter.Builder(context).build()
            val selectionFactory: ExoTrackSelection.Factory = AdaptiveTrackSelection.Factory()
            val trackSelector = DefaultTrackSelector(context, selectionFactory)
            val analyticsCollector = AnalyticsCollector(Clock.DEFAULT)
            mExoPlayer = SimpleExoPlayer.Builder(context)
                .setBandwidthMeter(bandwidthMeter)
                .setTrackSelector(trackSelector)
                .setAnalyticsCollector(analyticsCollector)
                .build()

            mExoPlayer.playWhenReady = false
            mExoPlayer.analyticsCollector.addListener(EventLogger(trackSelector))
            mExoPlayer.addListener(mEventListener)
        }
    }

    override fun prepare(uri: String) {
        mPlayerHandler.post {
            Log.d(TAG, "prepare")
            mExoPlayer.setMediaItem(MediaItem.fromUri(uri))
            mExoPlayer.prepare()
        }
    }

    override fun play() {
        mPlayerHandler.post {
            Log.d(TAG, "play")
            mExoPlayer.play()
        }
    }

    override fun pause() {
        mPlayerHandler.post {
            Log.d(TAG, "pause")
            mExoPlayer.pause()
        }
    }

    override fun resume() {
        mPlayerHandler.post {
            Log.d(TAG, "play")
            mExoPlayer.play()
        }
    }

    override fun seekTo(msec: Long) {
        mPlayerHandler.post {
            Log.d(TAG, "seekTo $msec")
            mExoPlayer.seekTo(msec)
        }
    }

    override fun stop() {
        mPlayerHandler.post {
            Log.d(TAG, "stop")
            mExoPlayer.stop()
        }
    }

    override fun release() {
        mPlayerHandler.post {
            Log.d(TAG, "release")
            mExoPlayer.release()
        }
    }

    override fun getAudioSessionId(): Int {
        return mExoPlayer.audioSessionId
    }

    override fun getDuration(): Long {
        return mExoPlayer.duration
    }

    override fun getContentPosition(): Long {
        return mExoPlayer.contentPosition
    }

    override fun getBufferedPosition(): Long {
        return mExoPlayer.bufferedPosition
    }

    fun getSpeed(): Float {
        return mExoPlayer.playbackParameters.speed
    }

    override fun setPlayerEventListener(listener: IPlayerEventListener?) {
        mPlayerEventListener = listener
    }

    override fun setAudioListener(listener: IAudioListener?) {
        mAudioListener = listener
    }

    inner class PlayerHandler @JvmOverloads constructor(looper: Looper = Looper.getMainLooper()) :
        Handler(looper) {

        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_PLAYER_PROGRESS -> {
                    if (mCurrentPlayState == PlaybackState.STATE_NONE
                        || mCurrentPlayState == PlaybackState.STATE_PAUSED
                    ) {
                        return
                    }

                    mPlayerEventListener?.also {
                        it.onBufferedSeekChanged(mExoPlayer.bufferedPosition, mExoPlayer.duration)
                        it.onPlayedSeekChanged(mExoPlayer.contentPosition, mExoPlayer.duration)
                    }

                    if (mExoPlayer.contentPosition < mExoPlayer.duration) {
                        sendEmptyMessageDelayed(
                            MSG_PLAYER_PROGRESS,
                            1000L
                        )
                    }
                }
                else -> {
                }
            }
        }
    }
}